package com.itheima.springbootsecurity.filter;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.itheima.springbootsecurity.domain.LoginUser;
import com.itheima.springbootsecurity.domain.User;
import com.itheima.springbootsecurity.exception.CustomAuthenticationException;
import com.itheima.springbootsecurity.handler.LoginFailureHandler;
import com.itheima.springbootsecurity.mapper.UserMapper;
import com.itheima.springbootsecurity.service.ResourceService;
import com.itheima.springbootsecurity.utils.JWTUtils;
import com.itheima.springbootsecurity.utils.ThreadLocalUtil;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.List;
import java.util.Objects;

/**
 * JWT认证过滤器
 * 每一个Servlet请求只会执行一次
 *
 * @author wuhaohua
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Autowired
    private JWTUtils jwtUtils;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ResourceService resourceService;

    @Autowired
    private LoginFailureHandler loginFailureHandler;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 认证过滤器
     *
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        // 拿到不需要校验权限的资源
        List<String> noAuthResourcePathList = resourceService.getNoAuthResourcePathList();


        try {
            String uri = request.getRequestURI();
            System.out.println("请求uri：" + uri);
            if (!noAuthResourcePathList.contains(uri)) {
                // 如果用户当前请求的url不在不需校验权限的资源列表中，则进行资源校验
                this.validateToken(request);
            }
        } catch (AuthenticationException e) {
            e.printStackTrace();
//            throw new CustomAuthenticationException("token校验失败");
            loginFailureHandler.onAuthenticationFailure(request, response, e);
        }

        // 对于通过验证的请求放行
        filterChain.doFilter(request, response);
    }

    /**
     * 验证token
     *
     * @param request 请求对象
     */
    private void validateToken(HttpServletRequest request) {
        String username = null;

        // 从头部获取token信息
        String token = request.getHeader(jwtUtils.getTokenHttpHeader());
        if (!StringUtils.hasLength(token)) {
            throw new CustomAuthenticationException("token为空");
        }
        // 从token中解析出用户名
        try {
            username = jwtUtils.verifyToken(token).get("username").toString();
        } catch (Exception e) {
            throw new CustomAuthenticationException("token校验失败");
        }

        System.out.println("当前访问用户名：" + username);
        // 检查redis中是否有当前token
        String redisToken = stringRedisTemplate.opsForValue().get(jwtUtils.getTokenRedisKeyPrefix() + username);
        if (!StringUtils.hasLength(redisToken) || !redisToken.equals(token)) {
            throw new CustomAuthenticationException("token已过期，请重新登录");
        }

        // 获取用户信息
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getUsername, username);
        User user = userMapper.selectOne(queryWrapper);
        if (Objects.isNull(user)) {
            throw new CustomAuthenticationException("当前用户不存在");
        }




        // 赋权操作，添加当前用户可以访问的接口标签
        List<String> authoritieList = resourceService.getPathListByUserid(user.getId());

        // 构建Security的UserDetails对象
        LoginUser loginUser = new LoginUser(user, authoritieList);

        // 构建用户认证信息
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        // 将认证信息放入SpringSecurity的上下文
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        // 将验证通过的用户信息放入ThreadLocal中
        ThreadLocalUtil.set(loginUser);
    }
}
